﻿package kliment.geom.color {
	import flash.events.Event;
	import flash.events.EventDispatcher;
	
	import kliment.utils._String;
	
	/**
	* ...
	* @author Kliment
	*/
	public class Color extends EventDispatcher {
		static public const RGB:int =  1;
		static public const CMYK:int = 2;
		static public const HSV:int =  4;
		static public const LAB:int =  8;
		static public const XYZ:int =  16;
		
		private var _rgb:RGBColor;
		private var _hsv:HSVColor;
		private var _lab:LABColor;
		private var _cmyk:CMYKColor;
		private var _xyz:XYZColor;
        
		private var _lastChanged:int = 0;
		private var _validSpaces:int = 0;
        
		public function Color(hex:uint = 0x000000){
			this.hex = hex;
		}
		
		public function clone():Color {
			var result:Color = new Color(hex);
			return result;
		}
		
		public function get hex():uint {
			if (!(_validSpaces & RGB)) _updateRGB();
			return _rgb.r << 16 | _rgb.g << 8 | _rgb.b;
		}
		
		public function set hex(hex:uint):void {
			if (!_rgb) _rgb = new RGBColor(this);
            _rgb.setValue(hex >> 16 & 255, hex >> 8 & 255, hex & 255);
		}
		
		public function get hexSmart():uint {
			if (!(_validSpaces & RGB)) _updateRGB();
			return _round3P(_rgb.r, 17) << 16 | _round3P(_rgb.g, 17) << 8 | _round3P(_rgb.b, 17);
		}
		
		public function get hexSafe():uint {
			if (!(_validSpaces & RGB)) _updateRGB();
			return _round3P(_rgb.r, 51) << 16 | _round3P(_rgb.g, 51) << 8 | _round3P(_rgb.b, 51);
		}
		
		public function get hexString():String { 
			var result:String = hex.toString(16);
			return _String.addZeroNumberStr(result, 6);
		}
		public function get hexSmartString():String { return _String.addZeroNumberStr(hexSmart.toString(16), 6) }
		public function get hexSafeString():String { return _String.addZeroNumberStr(hexSafe.toString(16), 6) }
		
		public function set hexString(hexStr:String):void {
            var val:int = parseInt(hexStr, 16);
            if (!isNaN(val)) 
            	hex = val;
        }
		
		public function get rgb():RGBColor { return _rgb }
		public function set rgb(value:RGBColor):void {
			if (_rgb) _rgb.color = null;
			value.color = this;
			_rgb = value;
		}
		
		public function get hsv():HSVColor { 
			if (!(_validSpaces & HSV)) 
				_updateHSV(); 
			return _hsv;
		}
		public function set hsv(value:HSVColor):void {
			if (_hsv) _hsv.color = null;
			value.color = this;
			_hsv = value;
		}
		
		public function get lab():LABColor { 
			if (!(_validSpaces & LAB)) 
				_updateLAB(); 
			return _lab;
		}
		public function set lab(value:LABColor):void {
			if (_lab) _lab.color = null;
			value.color = this;
			_lab = value;
		}
		
		public function get cmyk():CMYKColor { 
			if (!(_validSpaces & CMYK)) 
				_updateCMYK();
			return _cmyk;
		}
		public function set cmyk(value:CMYKColor):void {
			if (_cmyk) _cmyk.color = null;
			value.color = this;
			_cmyk = value;
		}
		
		public function get xyz():XYZColor { 
			if (!(_validSpaces & XYZ)) 
				_updateXYZ();
			return _xyz;
		}
		public function set xyz(value:XYZColor):void {
			if (_xyz) _xyz.color = null;
			value.color = this;
			_xyz = value;
		}
		
		private function _updateHSV():void {
			if (!(_validSpaces & RGB))
				_updateRGB();
			if (!_hsv)
				_hsv = new HSVColor(this);
			_rgb.toHSV(_hsv);
			_validSpaces = _validSpaces | HSV;
		}
		
		private function _updateXYZ():void {
			if (!(_validSpaces & RGB))
				_updateRGB();
			if (!_xyz)
				_xyz = new XYZColor(this);
			_rgb.toXYZ(_xyz);
			_validSpaces = _validSpaces | XYZ;
		}
		
		private function _updateCMYK():void{
			if (!(_validSpaces & RGB))
				_updateRGB();
			if (!_cmyk)
				_cmyk = new CMYKColor(this);
			_rgb.toCMYK(_cmyk);
			_validSpaces = _validSpaces | CMYK;
        }
		
		private function _updateLAB() : void{
			if (!(_validSpaces & XYZ)) 
				_updateXYZ();
			if (!_lab)
				_lab = new LABColor(this);
			_lab = _xyz.toLAB(_lab);
			_validSpaces = _validSpaces | LAB;
		}
		
		private function _updateRGB():void{
			if (!_rgb) 
				_rgb = new RGBColor(this);
			
			switch(_lastChanged){
				case CMYK:
					_updateRGBfromCMYK();
				break;
				case HSV:
					_updateRGBfromHSV();
				break;
				case LAB:
					_updateRGBfromLAB();
				break;
				case XYZ:
					_updateRGBfromXYZ();
				break;
			}
			_validSpaces = _validSpaces | RGB;
		}
		
		private function _updateRGBfromCMYK():void {
			_cmyk.toRGB(_rgb);
        }
		
		private function _updateRGBfromHSV():void {
			hsv.toRGB(_rgb);
		}
		
		private function _updateRGBfromLAB():void {
			if (!_xyz)
				_xyz = new XYZColor(this);
			
			_lab.toXYZColor(_xyz);
			_updateRGBfromXYZ();
        }
        
		private function _updateRGBfromXYZ():void {
			_xyz.toRGB(_rgb)
		}
		
		public function valueChanged(value:int):void{
			_validSpaces = value;
			_lastChanged = value;
			if (hasEventListener(Event.CHANGE)) 
				dispatchEvent(new Event(Event.CHANGE));
		}
		
		private function _round3P(color:Number, divisor:Number):Number {
			return Math.round(color / divisor) * divisor;
		}
		
	}
}